package com.sx.netty.nio.Demo01;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @Author Administrator
 * @Description
 * @Date 2020/11/2 17:30
 * <p>
 * 简单演示NIO网络编程
 */
public class NIOServer {
    public static void main(String[] args) throws Exception {

//        创建一个ServerSocketChannel -> ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//        获取一个selector对象,并打开选择器
        Selector selector = Selector.open();

//        绑定ip，端口，在服务端进行监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
//        设置为非阻塞
        serverSocketChannel.configureBlocking(false);

//        将serverSocketChannel注册到selector中，关心事件为OP_ACCEPT(?)
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

//        循环等待客户端的连接
        while (true) {
//            这里等待2s,如果没有事件发生，返回，轮询？
            if (selector.select(2000) == 0) {
                System.out.println("没有产生连接");
                continue;
            }
//            如果返回>0，则表示已经获取到相关的selectorKey集合，即获取到关注的事件
//            通过的selectedKey反向获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
//            迭代进行遍历
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
//                获取到selectedKey
                SelectionKey selectionKey = iterator.next();

//                判断通道发生的事件
//                如果是有新的连接产生，与上面的Selection.OP_ACCEPT对应
                if (selectionKey.isAcceptable()) {
//                    为该客户端产生一个SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId() + "\t" + "客户端连接成功，并产生一个socketChannel" + socketChannel.hashCode());
//                    将SocketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);
//                    将socketChannel注册到selector，关注事件是OP_READ，同时给socketChannel关联一个buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if (selectionKey.isReadable()) {//发生OP_READ事件
//                    通过selectionKey反转获取到对应的channel
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
//                    获取channel关联的buffer(也是通过selectKey获取)
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
//                    channel写入到buffer中
                    channel.read(buffer);
                    System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId() + "\t" + "from 客户端:\t" + new String(buffer.array()));
                }
//            手动移除当前selectionKey，防止重复操作
                iterator.remove();
            }
        }
    }
}
